package fsm

import (
	"encoding/json"
	"errors"
	"fmt"

	//"iot-base/common/common"

	"iot-base/common/common"
	//"iot-base/common/webapi"

	"github.com/aidarkhanov/nanoid/v2"
	"gorm.io/datatypes"
)

var cfgMap fSMCfgMap

func init() {
	//dbconn.DBConnection.AutoMigrate(&FSM{})
	cfgMap.SAFEMap = common.CreateSafeMap()
}

//FSM 数据库对象
type FSM struct {
	UID   string         `gorm:"type:varchar(40);primaryKey;" json:"uid"`
	SName string         `gorm:"type:varchar(50);not null;uniqueIndex:name_catalog;" json:"s_name"`
	Body  datatypes.JSON `json:"body"`
}

type FSMCfg struct {
	UID     string
	SName   string
	Events  []FSMEvent
	Actions []Action
}
type SAFEMap common.ThrdSafeMap
type fSMCfgMap struct {
	SAFEMap
}

//注册状态机
func RegisterFSM(pFSM *FSM) {
	var cfg FSMCfg
	cfg.UID = pFSM.UID
	cfg.SName = pFSM.SName
	json.Unmarshal(pFSM.Body, &cfg.Events)
	fmt.Println("*******************", len(cfg.Events), pFSM.SName, len(pFSM.Body))
	cfgMap.SAFEMap.Set(pFSM.UID, &cfg)
}

func (pCfg *FSMCfg) getEvent(name string) *FSMEvent {
	for k := range pCfg.Events {
		if pCfg.Events[k].Name == name {
			return &pCfg.Events[k]
		}
	}
	return nil
}

//获取配置
func GetCfg(fname string) *FSMCfg {
	icfg, ok := cfgMap.SAFEMap.Get(fname)
	if ok && icfg != nil {
		cfg, ok := icfg.(*FSMCfg)
		if ok && cfg != nil {
			return cfg
		}
	}
	return nil
}

//获取事件
func (pCfg *FSMCfg) GetAction(name string) Action {
	if pCfg.Actions != nil {
		for _, action := range pCfg.Actions {
			if action.GetName() == name {
				return action
			}
		}
	}
	return nil
}

func createUID() string {
	v, _ := nanoid.New()
	return fmt.Sprintf("%v", v)
}

//GetID 获取id
func (pObj *FSM) GetID() interface{} {
	return pObj.UID
}

//GetName 获取名称
func (pObj *FSM) GetName() string {
	return pObj.SName
}

//AutoID 自动生成id
func (pObj *FSM) AutoID() {
	v, _ := nanoid.New()
	pObj.UID = fmt.Sprintf("%v", v)
}

//notifier 状态变化后通知
type Notifier interface {
	End()
	StateChanged(transitionn Transition, pcontext *FSMInstance)
}

//Transition 状态变化
type Transition struct {
	From string
	To   string
}

//FSMEvent 状态机事件
type FSMEvent struct {
	Name        string       `json:"name"`
	ActionNames []string     `json:"actionNames"`
	Transitions []Transition `json:"transitions"`
	//Condition   string       `json:"condition"`
	IsErrorEnd bool `json:"is_eroor_end"`
}

type msg struct {
	name  string
	param interface{}
}

//FSMInstance 上下文
type FSMInstance struct {
	stateName    string
	InstanceName string
	FSMID        string
	pContext     SAFEMap
	msgChan      chan msg
}
type DefaultAction struct {
	name     string
	callback func(*FSMInstance) error
	param    interface{}
}

func (pAction *DefaultAction) GetName() string {
	return pAction.name
}

func (pAction *DefaultAction) SetParams(param interface{}) {
	pAction.param = param
}

func (pAction *DefaultAction) Execute(pInstance *FSMInstance) error {
	if pAction.callback != nil {
		return pAction.callback(pInstance)
	}
	return nil
}

func RegisterAction(fsmID string, pAction Action) {
	pCfg := GetCfg(fsmID)
	if pCfg != nil {
		if pCfg.Actions == nil {
			pCfg.Actions = make([]Action, 0, 1)
		}
		pCfg.Actions = append(pCfg.Actions, pAction)
	} else {
		fmt.Println("cfg not found", fsmID)
	}
}

func RegisterActionAsFun(fsmID string, name string, fun func(*FSMInstance) error) {
	pAction := &DefaultAction{
		name:     name,
		callback: fun,
	}
	RegisterAction(fsmID, pAction)
}

//Action 动作
type Action interface {
	Execute(pInstance *FSMInstance) error
	SetParams(params interface{})
	GetName() string
}

//Check 检查
func (pEvent *FSMEvent) Check(pContext SAFEMap) bool {
	return true
}

//CreateFSMInstance 创建fsm实例
func CreateFSMInstance(fsmID string) *FSMInstance {
	pInstance := &FSMInstance{
		FSMID:        fsmID,
		InstanceName: fsmID + "." + createUID(),
		pContext:     common.CreateSafeMap(),
		msgChan:      make(chan msg, 1),
	}
	return pInstance
}

func (pInstance *FSMInstance) GetState() string {
	return pInstance.stateName
}

//RunEvent 运行动作
func (pInstance *FSMInstance) RunEvent(nmsg msg) error {
	//fid := pInstance.FSMID
	pCfg := pInstance.GetCfg()
	if pCfg == nil {
		return errors.New("fsm cfg not found")
	}
	pEvent := pCfg.getEvent(nmsg.name)
	if pEvent == nil {
		fmt.Println(pInstance.FSMID, ":event not found")
		return errors.New(pInstance.FSMID + ":event not found")
	}

	if pEvent.Check(pInstance.pContext) {
		fmt.Println(pInstance.FSMID, ":run actions ---------------", nmsg.param)
		err := pInstance.runActions(pEvent, nmsg.param)
		return err
	}

	return nil
}

//IsEnd 是否完成
func (pcontext *FSMInstance) IsEnd() bool {
	return pcontext.stateName == "END" || pcontext.stateName == "end"
}

func (pInstance *FSMInstance) GetCfg() *FSMCfg {
	fsmID := pInstance.FSMID
	return GetCfg(fsmID)
}

//GetAction 获取action
func (pInstance *FSMInstance) GetAction(name string) Action {
	pCfg := pInstance.GetCfg()
	if pCfg != nil {
		return pCfg.GetAction(name)
	}
	return nil
}

//RunAction 运行action
func (pInstance *FSMInstance) RunAction(name string, param interface{}) error {
	pActionInterface := pInstance.GetAction(name)
	pAction, ok := pActionInterface.(Action)

	if ok && pAction != nil {
		pAction.SetParams(param) //参数
		return pAction.Execute(pInstance)
	}
	fmt.Println("action not found", name)
	return errors.New("action not found " + name)

}

func (pcontext *FSMInstance) runActions(pEvent *FSMEvent, param interface{}) error {
	for _, actionName := range pEvent.ActionNames {
		fmt.Println("run action -------", actionName)
		err := pcontext.RunAction(actionName, param)
		if err != nil {
			if pEvent.IsErrorEnd {
				pcontext.stateName = "END"
				fmt.Println("error happened to end")
			}
			return err
		}
	}
	for _, transition := range pEvent.Transitions {
		fmt.Println("state changing -------", transition.From, transition.To, pcontext.stateName)
		if transition.From == "*" || transition.From == pcontext.stateName {
			pcontext.stateName = transition.To
			fmt.Println("state changed to-------", transition.To)
			break
		}
	}

	return nil
}
func (pInstance *FSMInstance) RunLoop() {
	//pInstance.msgChan =
	for {
		msg := <-pInstance.msgChan
		if len(msg.name) > 0 {
			fmt.Println(pInstance.FSMID, "event-------", msg)
			pInstance.RunEvent(msg)
			if pInstance.IsEnd() {
				fmt.Println(pInstance.FSMID, "event end------------")
				break
			}
		}

		fmt.Println(pInstance.FSMID, "event finished------------", msg.name)
	}
}
func (pInstance *FSMInstance) GetContext() SAFEMap {
	return pInstance.pContext
}

func (pInstance *FSMInstance) NotifyEvent(name string) {
	pInstance.msgChan <- msg{
		name: name,
	}
}

//NotifyEventWithParam 通知事件发送，并携带参数
func (pInstance *FSMInstance) NotifyEventWithParam(name string, param interface{}) {
	pInstance.msgChan <- msg{
		name:  name,
		param: param,
	}
}

//InitAPI 初始化api
/*
func InitAPI(cfg *gin.RouterGroup) {
	webapi.BindCrudEx(cfg, "/fsm/", &FSM{}, nil, nil)
}*/
